// Copyright (c) 2024 CHANGLEI. All rights reserved.

import 'package:charts_grasp/src/chart/utils/enum.dart';
import 'package:charts_grasp/src/circular_chart/utils/enum.dart';
import 'package:charts_grasp/src/common/utils/enum.dart';
import 'package:charts_grasp/src/common/utils/typedef.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';

class ChartContainer extends SingleChildRenderObjectWidget {
  const ChartContainer({super.key, required Widget child}) : super(child: child);

  @override
  RenderObject createRenderObject(BuildContext context) {
    return _ChartContainerBox();
  }
}

class _ChartContainerBox extends RenderShiftedBox {
  _ChartContainerBox() : super(null);
  double minHeight = 300;
  double minWidth = 300;

  @override
  void performLayout() {
    double height = constraints.maxHeight;
    double width = constraints.maxWidth;
    if (height == double.infinity) {
      height = minHeight;
    }
    if (width == double.infinity) {
      width = minWidth;
    }
    child!.layout(BoxConstraints(
      maxHeight: height,
      maxWidth: width,
    ),);
    size = Size(width, height);
  }

}

class ChartTitle {
  ChartTitle(
      {this.text = '',
      this.textStyle,
      this.alignment = ChartAlignment.center,
      this.borderColor = Colors.transparent,
      this.borderWidth = 0,
      this.backgroundColor,});

  final String text;

  final TextStyle? textStyle;

  final ChartAlignment alignment;

  final Color? backgroundColor;

  final Color borderColor;

  final double borderWidth;

  @override
  bool operator ==(Object other) {
    if (identical(this, other)) {
      return true;
    }
    if (other.runtimeType != runtimeType) {
      return false;
    }

    return other is ChartTitle &&
        other.textStyle == textStyle &&
        other.alignment == alignment &&
        other.borderColor == borderColor &&
        other.borderWidth == borderWidth &&
        other.backgroundColor == backgroundColor;
  }

  @override
  int get hashCode {
    final List<Object?> values = <Object?>[text, textStyle, alignment, borderColor, borderWidth, backgroundColor];
    return Object.hashAll(values);
  }
}

@immutable
class Legend {
  const Legend(
      {bool? isVisible,
      LegendPosition? position,
      ChartAlignment? alignment,
      this.backgroundColor,
      Color? borderColor,
      double? borderWidth,
      double? opacity,
      this.height,
      this.width,
      double? padding,
      double? iconHeight,
      double? iconWidth,
      bool? shouldAlwaysShowScrollbar,
      bool? toggleSeriesVisibility,
      this.textStyle,
      bool? isResponsive,
      LegendItemOrientation? orientation,
      LegendTitle? title,
      LegendItemOverflowMode? overflowMode,
      this.legendItemBuilder,
      Color? iconBorderColor,
      double? iconBorderWidth,
      double? itemPadding,
      this.offset,
      this.image,})
      : isVisible = isVisible ?? false,
        shouldAlwaysShowScrollbar = shouldAlwaysShowScrollbar ?? false,
        position = position ?? LegendPosition.auto,
        alignment = alignment ?? ChartAlignment.center,
        borderColor = borderColor ?? Colors.transparent,
        borderWidth = borderWidth ?? 0.0,
        iconBorderColor = iconBorderColor ?? Colors.transparent,
        iconBorderWidth = iconBorderWidth ?? 0.0,
        opacity = opacity ?? 1.0,
        padding = padding ?? 10.0,
        iconHeight = iconHeight ?? 12.0,
        iconWidth = iconWidth ?? 12.0,
        toggleSeriesVisibility = toggleSeriesVisibility ?? true,
        isResponsive = isResponsive ?? false,
        orientation = orientation ?? LegendItemOrientation.auto,
        overflowMode = overflowMode ?? LegendItemOverflowMode.scroll,
        itemPadding = itemPadding ?? 15.0,
        title = title ?? const LegendTitle();

  final bool? isVisible;

  final LegendPosition position;

  final ChartAlignment alignment;

  final Color? backgroundColor;

  final bool shouldAlwaysShowScrollbar;

  final Color borderColor;

  final double borderWidth;

  final Color iconBorderColor;

  final double iconBorderWidth;

  final double opacity;

  final String? height;

  final String? width;

  final double padding;

  final double iconHeight;

  final double iconWidth;

  final bool toggleSeriesVisibility;

  final TextStyle? textStyle;

  final bool isResponsive;

  final LegendItemOrientation orientation;

  final LegendTitle title;

  final LegendItemBuilder? legendItemBuilder;

  final LegendItemOverflowMode overflowMode;

  final double itemPadding;

  final Offset? offset;

  final ImageProvider? image;

  @override
  bool operator ==(Object other) {
    if (identical(this, other)) {
      return true;
    }
    if (other.runtimeType != runtimeType) {
      return false;
    }

    return other is Legend &&
        other.isVisible == isVisible &&
        other.position == position &&
        other.alignment == alignment &&
        other.backgroundColor == backgroundColor &&
        other.borderColor == borderColor &&
        other.borderWidth == borderWidth &&
        other.opacity == opacity &&
        other.height == height &&
        other.width == width &&
        other.padding == padding &&
        other.iconHeight == iconHeight &&
        other.iconWidth == iconWidth &&
        other.toggleSeriesVisibility == toggleSeriesVisibility &&
        other.textStyle == textStyle &&
        other.isResponsive == isResponsive &&
        other.orientation == orientation &&
        other.title == title &&
        other.overflowMode == overflowMode &&
        other.legendItemBuilder == legendItemBuilder &&
        other.iconBorderColor == iconBorderColor &&
        other.iconBorderWidth == iconBorderWidth &&
        other.itemPadding == itemPadding &&
        other.image == image;
  }

  @override
  int get hashCode {
    final List<Object?> values = <Object?>[
      isVisible,
      position,
      alignment,
      backgroundColor,
      borderColor,
      borderWidth,
      opacity,
      height,
      width,
      padding,
      iconHeight,
      iconWidth,
      toggleSeriesVisibility,
      textStyle,
      isResponsive,
      orientation,
      title,
      overflowMode,
      legendItemBuilder,
      iconBorderColor,
      iconBorderWidth,
      itemPadding,
      image,
    ];
    return Object.hashAll(values);
  }
}

class LegendRenderer {
  LegendRenderer(this.legend);

  final Legend? legend;

  late LegendPosition legendPosition;

  late LegendItemOrientation orientation;
}

class MeasureWidgetContext {
  MeasureWidgetContext({this.context, this.key, this.widget, this.seriesIndex, this.pointIndex});

  BuildContext? context;

  int? seriesIndex;

  int? pointIndex;

  Key? key;

  Size? size;

  Widget? widget;

  bool isRender = false;
}

@immutable
class LegendTitle {
  const LegendTitle({this.text, this.textStyle, ChartAlignment? alignment})
      : alignment = alignment ?? ChartAlignment.center;

  final String? text;

  final TextStyle? textStyle;

  final ChartAlignment alignment;

  @override
  bool operator ==(Object other) {
    if (identical(this, other)) {
      return true;
    }
    if (other.runtimeType != runtimeType) {
      return false;
    }

    return other is LegendTitle && other.textStyle == textStyle && other.alignment == alignment && other.text == text;
  }

  @override
  int get hashCode {
    final List<Object?> values = <Object?>[text, textStyle, alignment];
    return Object.hashAll(values);
  }
}

class EmptyPointSettings {
  EmptyPointSettings(
      {this.color = Colors.grey,
      this.mode = EmptyPointMode.gap,
      this.borderColor = Colors.transparent,
      this.borderWidth = 0,});

  final Color color;

  final Color borderColor;

  final double borderWidth;

  final EmptyPointMode mode;

  @override
  bool operator ==(Object other) {
    if (identical(this, other)) {
      return true;
    }
    if (other.runtimeType != runtimeType) {
      return false;
    }

    return other is EmptyPointSettings &&
        other.color == color &&
        other.mode == mode &&
        other.borderColor == borderColor &&
        other.borderWidth == borderWidth;
  }

  @override
  int get hashCode {
    final List<Object?> values = <Object?>[color, mode, borderColor, borderWidth];
    return Object.hashAll(values);
  }
}

class PointInfo<D> {
  PointInfo(this.x, this.y);

  dynamic x;

  num? y;

  String? text;

  late Color fill;

  late Color color;

  late Color borderColor;

  D? sortValue;

  late num borderWidth;

  bool isExplode = false;

  bool isShadow = false;

  bool isEmpty = false;

  bool isVisible = true;

  bool isSelected = false;

  Position? dataLabelPosition;

  ChartDataLabelPosition? renderPosition;

  Rect? labelRect;

  bool _isLabelCollide = false;

  Size dataLabelSize = Size.zero;

  bool saturationRegionOutside = false;

  late num yRatio;

  late num heightRatio;

  late List<Offset> pathRegion;

  Rect? region;

  late Offset symbolLocation;

  num? explodeDistance;

  bool isTooltipRenderEvent = false;

  bool labelRenderEvent = false;
}

class PointInfoHelper {
  const PointInfoHelper._();

  static bool getIsLabelCollide(PointInfo<dynamic> point) => point._isLabelCollide;

  static void setIsLabelCollide(PointInfo<dynamic> point, bool isLabelCollide) {
    point._isLabelCollide = isLabelCollide;
  }
}
